package jblip.util.json;

public class JSONString implements JSONValue<String> {

  private final String raw;
  private final String value;

  public JSONString(String str) throws JSONException {
    if(str == null) {
      throw new NullPointerException("Constructor parameter is null");
    }
    
    if(str.startsWith("\"") && str.endsWith("\"")) {
      str = str.substring(1, str.length() - 1);
    }

    final StringBuilder builder = new StringBuilder(str.length());

    for(int i = 0; i < str.length(); i++) {
      char next = str.charAt(i);
      
      if(next == '\\') {
        if(++i >= str.length()) {
          throw new JSONException("Illegal end of string: \\");
        }

        next = str.charAt(i);

        switch(next) {
          case '"':
          case '/':
          case '\\': builder.append(next); break;
          case 'b': builder.append('\b'); break;
          case 'f': builder.append('\f'); break;
          case 'n': builder.append('\n'); break;
          case 'r': builder.append('\r'); break;
          case 't': builder.append('\t'); break;
          case 'u':
            if(i + 4 >= str.length()) {
              throw new JSONException("Illegal end of string: " + str.substring(i-1));
            }
            
            int charval = 0;
            
            for(int j = 1; j <= 4; j++) {
              charval = (charval << 4) | hexvalue(str.charAt(i + j));
            }
            
            builder.append((char)charval);
            i += 4;
            break;
          default:
            throw new JSONException("Can't parse '" + next + "' as a control character");
        }
      } else if (next == '"') {
        throw new JSONException("Non-escaped quotation mark in \"" + str + "\" at position " + i);
      } else {
        builder.append(next);
        if(str.charAt(i) != next) { // surrogate at the position, skip next
          i++;
        }
      }
    }
    
    this.raw = str;
    this.value = builder.toString();
  }

  private int hexvalue(char c) throws JSONException {
    if(c >= '0' && c <= '9') {
      return (c - '0');
    }
    
    if(c >= 'a' && c <= 'f') {
      return (c + 10 - 'a');
    }
    
    if(c >= 'A' && c <='F') {
      return (c + 10 - 'A');
    }
    
    throw new JSONException("Can't parse '" + c + "' as a hexadecimal digit");
  }

  public String getValue() {
    return value;
  }
  
  @Override
  public boolean equals(Object obj) {
    if (obj instanceof String) {
      return obj.equals(value);
    } else if (obj instanceof JSONString) {
      JSONString jsonstr = (JSONString) obj;
      return jsonstr.getValue().equals(value);
    } else {
      return false;
    }
  }
  
  @Override
  public int hashCode() {
    return value.hashCode();
  }
  
  @Override
  public String toString() {
    return '"' + this.raw + '"';
  }

}
